package com.hmdp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.hmdp.common.utils.RedisConstants;
import com.hmdp.common.utils.ScrollResult;
import com.hmdp.common.utils.UserHolder;
import com.hmdp.dto.Result;
import com.hmdp.dto.UserDto;
import com.hmdp.entity.Blog;
import com.hmdp.entity.Follow;
import com.hmdp.entity.User;
import com.hmdp.mapper.BlogMapper;
import com.hmdp.service.IBlogService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.service.IFollowService;
import com.hmdp.service.IUserService;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {

    @Resource
    private IUserService userService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private IFollowService followService;

    @Override
    public Result queryBlogById(Long id) {
        if (id == null) {
            return Result.fail("参数传入异常");
        }
        Blog blog = getById(id);
        if (blog == null) {
            return Result.fail("博客不存在");
        }

        Long blogUserId = blog.getUserId();
        User user = userService.getById(blogUserId);
        blog.setIcon(user.getIcon());
        blog.setName(user.getNickName());
        //判断是否点赞
        String likedKey = RedisConstants.BLOG_LIKED_KEY + id;

        Long currentUserId = UserHolder.getUser().getId();
        Double member = stringRedisTemplate.opsForZSet().score(likedKey, currentUserId.toString());
        //分数值不为null即已点赞
        blog.setIsLike(member != null);

        return Result.ok(blog);
    }

    @Override
    public Result likeBlog(Long id) {
//  获取登录用户
        Long userId = UserHolder.getUser().getId();
        if (userId == null) {
            throw new RuntimeException("读取用户信息错误");
        }
        String likedKey = RedisConstants.BLOG_LIKED_KEY + id;
//  判断是否点赞
        Double score = stringRedisTemplate.opsForZSet().score(likedKey, userId.toString());
        if (score == null) {
//        点赞
            boolean isSuccess = lambdaUpdate().eq(Blog::getId, id).setSql("liked = liked + 1").update();
//        添加集合元素
            if (isSuccess) {
                stringRedisTemplate.opsForZSet().add(likedKey, userId.toString(), System.currentTimeMillis());
            }

        } else {
//        取消点赞
            boolean isSuccess = lambdaUpdate().eq(Blog::getId, id).setSql("liked = liked - 1").update();
//        去除集合元素
            if (isSuccess) {
                stringRedisTemplate.opsForZSet().remove(likedKey, userId.toString());
            }
        }
        return Result.ok();
    }

    @Override
    public Result queryBlogLikes(Long id) {
//        查询点赞列表
        String likedKey = RedisConstants.BLOG_LIKED_KEY + id;
        int count = 5;
        Set<String> range = stringRedisTemplate.opsForZSet().range(likedKey, 0, --count);
        if (range == null || range.isEmpty()) {
            return Result.ok(Collections.emptyList());
        }
        List<Long> ids = range.stream().map(Long::valueOf).collect(Collectors.toList());
//        Collections.reverse(ids);
        List<UserDto> userDtoList = userService.lambdaQuery()
                .in(User::getId, ids)
                .last("ORDER BY FIELD (id," + StrUtil.join(",", ids) + ")")
                .list().stream()
                .map((item) -> BeanUtil.copyProperties(item, UserDto.class))
                .collect(Collectors.toList());
        return Result.ok(userDtoList);
    }

    @Override
    public Result saveBlog(Blog blog) {
        // 获取登录用户
        UserDto user = UserHolder.getUser();
        blog.setUserId(user.getId());
        // 保存探店博文
        boolean isSuccess = save(blog);
        if (isSuccess) {
            //存储到redis的收件箱sortedSet 中，以实现 feed流
            //获取关注的用户列表
            List<Follow> followList = followService.lambdaQuery().eq(Follow::getFollowUserId, blog.getUserId()).list();
            followList.forEach(follow -> {
                Long userId = follow.getUserId();
                String InBoxKey = RedisConstants.INBOX_KEY_PREFIX + userId;
                //推送到收件箱
                stringRedisTemplate.opsForZSet().add(InBoxKey, blog.getId().toString(), System.currentTimeMillis());
            });
            //发送笔记id到关注用户的收件箱
        }

        // 返回id
        return Result.ok(blog.getId());
    }

    @Override
    public Result scrollQuery(Long max, Integer offset) {
        if (max == null) {
            max = System.currentTimeMillis();
        }
        int count = 3;
        //滚动查询 ID
        Long userID = UserHolder.getUser().getId();
        if (userID == null) {
            throw new RuntimeException("读取用户信息错误");
        }

        String key = RedisConstants.INBOX_KEY_PREFIX + userID;
        Set<ZSetOperations.TypedTuple<String>> range = stringRedisTemplate.opsForZSet()
                .reverseRangeByScoreWithScores(key, 0, max, offset, count);
        //计算offset
        if (range == null || range.isEmpty()) {
            return Result.ok();
        }
        List<Long> ids = new ArrayList<>(range.size());
        Long afterScore = 0L;
        int afterCount = 0;
        for (ZSetOperations.TypedTuple<String> typedTuple : range) {
            long score = 0;
            ids.add(Long.valueOf(Objects.requireNonNull(typedTuple.getValue())));
            score = Objects.requireNonNull(typedTuple.getScore()).longValue();


            if (afterScore.equals(score)) {
                afterCount++;
            } else {
                afterScore = score;
                afterCount = 1;
            }
        }

        //封装实体
        ScrollResult scrollResult = new ScrollResult();
        scrollResult.setOffset(afterCount);
        scrollResult.setMinTime(afterScore);
        List<Blog> list = lambdaQuery()
                .in(Blog::getId, ids)
                .last("ORDER BY FIELD(id,"+StrUtil.join(",",ids)+")")
                .list();
        scrollResult.setList(list);
        return Result.ok(scrollResult);
    }
}
